assume ds:data, es:table, cs:code

data segment
    db 10 dup(0)       
data ends
table segment  
    db '1975'
    db ' '
    dd 100  ;以上是表示21年 公司总收入的1个dword型数据
    db ' '
    dw 1    ;以上是表示21公司雇员人数的1个Word型数据。
    db ' '
    dw 100    
    db ' '
   
    db '1975'
    db ' '
    dd 5937000  ;以上是表示21年 公司总收入的1个dword型数据
    db ' '
    dw 17800    ;以上是表示21公司雇员人数的1个Word型数据。
    db ' '
    dw 33 
    db ' '
table ends  

code segment
;-----------
; 主程序
;---------
start:
    mov ax,table
    mov es,ax     ;获取table 数据段es
    mov bx,data
    mov ds,bx     ;获取数据段 ds

    mov si,0      ;读取table累积计数器
    mov cx,2     ;循环次数
    mov bx,0      ;读取table偏移地址
    mov dh,3      ;屏幕的行数  测试的时候可以写3以上的数字，写0换行看不到效果 这里是个大坑，dh 复制相当于修改了dx 需要对dx做保护

main_read:
    push cx 
    ;年份  分别调试一
    mov ax,es:[bx]    ;读取年份数据前两位，年份是字符串直接放到内存中
    mov ds:[si],ax
    mov ax,es:[bx+2]  ;读取年份数据后两位
    mov ds:[si+2],ax
    mov byte ptr ds:[si+4], 0 

    mov dl,0           ;所在列的数,show_str的参数
    mov ch, 0          ;ch清零，防止高8位不为零。
    mov cl, 2  
    call show_str
    
    ; 收入 分别调试2
    push dx           ;大坑 在之前赋值了dh为3
    mov ax,es:[bx+5]  ;收入的第一个字节 低16位
    mov dx,es:[bx+5+2];收入的第二个字节 高16
    call dtoc
    pop dx   

    mov dl,20          ;所在列的数,show_str的参数
    mov ch, 0          ;ch清零，防止高8位不为零。
    mov cl, 2  
    call show_str

    ; 雇员
    push dx            ;大坑 在之前赋值了dh为3
    mov ax,es:[bx+10]
    mov dx,0           ;dx需要置为零
    call dtoc
    pop dx   

    mov dl,40          ;所在列的数,show_str的参数
    mov ch, 0          ;ch清零，防止高8位不为零
    mov cl, 2  
    call show_str

    ; 人均
    push dx            ;大坑 在之前赋值了dh为3
    mov ax,es:[bx+13] 
    mov dx,0
    call dtoc
    pop dx   

    mov dl,50        ;所在列的数,show_str的参数
    mov ch,0          ;ch清零，防止高8位不为零
    mov cl,2  
    call show_str

    add bx,16            ; 一行内存是16个字节
    mov si,0
    add dh,1
     ; 收入 分别调试3
    pop cx
    loop main_read 

   
   mov ax,4c00H
   int 21H



;---------------
;将dword数字转为字符串
;
;参数: ax dword低位
;参数: dx dword高位
;返回：无
;---------------
dtoc:
    push ax
    push dx
    push cx
    push bx ;divdw中使用到了bx
    push bp ;divdw中使用到了bp
    push si ;数据入栈保护

    
dtoc_count:
    mov cx,10    ;除数为零
    mov bx,0     ;在divdw中使用过，恢复为零
    mov bp,0
    call divdw   ;返回：ax 结果的低16位 dx 结果的低16位 bp 余数 

    push ax      ;我在这个地方调试了好久
    push dx      ;保护数据，一会需要做加法判断是否为零

    add ax,dx    
    mov cx,ax    ;判断商为是否为零

    pop dx
    pop ax       ;跳出之前要恢复原来的数值

    jcxz dtoc_last    ;跳出，数字循环完毕
    add bp,30H   ;获取十进制数码对应的ASII码
    push bp      ;存储余数
    
    inc si       ;在栈中有多少个数据 解决题5
    jmp short dtoc_count ;继续读取字符串

;处理边界值 最后一次商为零，也需要压栈
dtoc_last: 
    add bp,30H    ;字符零，ASCII是30h
    push bp       ;边界值为零
    inc si  

    mov cx,si        ;loop 循环读取存储的字符数据  关键点
    mov si,0         ;数据偏移地址
dtoc_write:
    pop ds:[si] ;将asii写入数据段
    inc si
    loop dtoc_write
exitdtoc:
    pop si 
    pop bp 
    pop bx 
    pop cx
    pop dx
    pop ax
    ret
;-------------
;被除数是32位的除法
;
;参数：ax dword低16位
;参数：dx dword高16位
;参数：cx 除数
;返回：ax 结果的低16位 
;返回：dx 结果的低16位
;返回：bp 余数 不在使用cx避免混乱
;-------------
divdw:                      ;子程序开始
    push ax             ;将被除数低16位先压栈保存。
    mov ax, dx          ;（ax）=（dx）
    mov dx, 0000H       ;
    div cx              ;此时（dx）=0000H，（ax）=000FH，组合成0000000FH。
    mov bx, ax          ;将H/N结果的商先保存在bx中，（bx）=0001H
    
    pop ax              ;将L值弹栈到ax
    div cx              ;此时（dx）=0005H，（ax）=4240H,组合成54240H
    mov bp, dx          ;返回值（cx）等于最终结果的余数
    mov dx, bx          ;最终结果高16位值=（bx）

    ret

;---------------
;屏幕指定位置显示字符串
;
;参数dh 屏幕的行数
;参数dl 所在行的列数
;参数cl ; 我们暂时固定属性 颜色属性（此处应是二进制数0000 0010） 
;需要临时数据段地址
;---------------
show_str: 
    push ax
    push dx
    push es ;保护es
    push cx
    push bx 
    push bp
    push si 

    mov ax,0B800H ;在汇编源程序中，数据不能以字母开头，所以要在前面加0。
    mov es,ax     ;显示缓冲区的段地址
    
    ;计算dl * 2 
    mov ax,0
    mov al,2
    mul dl
    mov bx,ax ;把结果放到dl

    ; 计算 dh * 160 结果在ax中
    mov ax,0   ;（ax）= 0，防止高位不为零  
    mov al,160 ;0a0H
    mul dh
    add bx,ax  ;获取最终否偏移量

    mov di,0   ;di作为字符的偏移零
    mov al,cl  ; 将属性放到al中，因为后边的程序会用到cx
show_str_read:
    mov cl,ds:[si]
    mov ch,0             ;cx中 ch固定为零，关键在于cl的变化是否为零 
    jcxz show_str_read_ok              ;跳出，字符串操作完毕
    mov es:[bx+di+0],cl  ;对字符串业务操作 写入字符
    mov es:[bx+di+1],al  ; 写入属性
    inc si               ;下一个字符串
    add di,2        
    jmp short show_str_read       ;继续读取字符串
show_str_read_ok:
    pop si 
    pop bp 
    pop bx 
    pop cx
    pop es
    pop dx
    pop ax
    ret

code ends
end start